package com.example.comment.service.impl;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.example.comment.base.dto.LikeDTO;
import com.example.comment.base.entity.CommentLike;
import com.example.comment.base.vo.CommentLikeVO;
import com.example.comment.convert.CommentLikeConvert;
import com.example.comment.mq.config.CommentMqProperties;
import com.example.comment.repository.CommentLikeRepository;
import com.example.comment.service.CommentLikeService;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author QingZeyu
 * @date 2024/5/7 11:07
 */
@Service
public class CommentLikeServiceImpl implements CommentLikeService {
    private static final String KEY_PREFIX = "LIKE:";

    private static final String LOCK_PREFIX = "LOCK:";

    private static final Long TIMEOUT = 5L;

    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    @Resource
    private CommentLikeRepository commentLikeRepository;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public Boolean checkIfLike(Long commentId, Long userId, Long referenceId) {
        String key = this.getKey(commentId, referenceId);
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            return redisTemplate.opsForSet().isMember(key, userId.toString());
        }else {
            //不查询数据库，若redis不存在，则认为不存在，查询数据库的操作移动到like方法中
            return Boolean.FALSE;
        }
    }

    @Override
    public Long getLikeSize(Long commentId, Long referenceId) {
        String key = this.getKey(commentId, referenceId);
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            return redisTemplate.opsForSet().size(key);
        }
        //可能有redis宕机的情况，但是更多是当前评论没有点赞。大部分的评论是没有点赞的，因此不考虑每一条评论存储一个set在redis中，存储过多的评论，反而占用过多的redis空间，就算宕机未恢复，那么丢失的只是点赞的信息，影响不是特别大；
        // 也不去用count查询数据库，给数据库减轻压力。
        // 具体看能否接受点赞数的缺失，重要的话就去查询数据库，使用count(id)，加快查询速度
        return 0L;
    }

    @Override
    public CommentLikeVO getByCommentIdAndUserId(Long commentId, Long userId, Long referenceId) {
        return CommentLikeConvert.INSTANCE.convertToVO(this.getCommentLike(commentId,userId,referenceId));
    }

    @Override
    public Boolean like(LikeDTO dto) {
        String key = this.getKey(dto.getCommentId(), dto.getReferenceId());
        RLock lock = redissonClient.getLock(LOCK_PREFIX+key);
        try {
            if (lock.tryLock(TIMEOUT, TimeUnit.SECONDS)) {
                if (checkIfLike(dto.getCommentId(), dto.getUserId(), dto.getReferenceId())) {
                    redisTemplate.opsForSet().remove(key, dto.getUserId().toString());
                    CommentLike commentLike = this.getCommentLikeWithoutDelFlag(dto.getCommentId(), dto.getUserId(), dto.getReferenceId());
                    commentLike.setUpdatedTime(new Date());
                    commentLike.setUpdatedBy(dto.getUserId());
                    commentLike.setDelFlag(Boolean.TRUE);
                    return commentLikeRepository.updateById(commentLike);
                } else {
                    redisTemplate.opsForSet().add(key, dto.getUserId().toString());
                    return this.commentLikeSaveOrUpdate(dto);
                }
            } else {
                return Boolean.FALSE;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return Boolean.FALSE;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private Boolean commentLikeSaveOrUpdate(LikeDTO dto){
        CommentLike commentLike = this.getCommentLikeWithoutDelFlag(dto.getCommentId(), dto.getUserId(), dto.getReferenceId());
        if (Objects.isNull(commentLike)) {
            CommentLike like = new CommentLike();
            like.setCommentId(dto.getCommentId());
            like.setUserId(dto.getUserId());
            like.setReferenceId(dto.getReferenceId());
            like.setCreatedBy(dto.getUserId());
            return commentLikeRepository.save(like);
        }else {
            commentLike.setDelFlag(Boolean.FALSE);
            return commentLikeRepository.updateById(commentLike);
        }
    }

    private CommentLike getCommentLike(Long commentId, Long userId, Long referenceId) {
        return commentLikeRepository.lambdaQuery()
                .eq(CommentLike::getCommentId,commentId)
                .eq(CommentLike::getUserId,userId)
                .eq(CommentLike::getReferenceId,referenceId)
                .eq(CommentLike::getDelFlag,Boolean.FALSE)
                .one();
    }

    private CommentLike getCommentLikeWithoutDelFlag(Long commentId, Long userId, Long referenceId) {
        return commentLikeRepository.lambdaQuery()
                .eq(CommentLike::getCommentId,commentId)
                .eq(CommentLike::getUserId,userId)
                .eq(CommentLike::getReferenceId,referenceId)
                .one();
    }

    private String getKey(Long commentId, Long referenceId) {
        return KEY_PREFIX + referenceId + ":" + commentId;
    }
}
